package com.ly.friend.service.user.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ly.common.core.constants.CacheConstants;
import com.ly.common.core.constants.Constants;
import com.ly.common.core.constants.HttpConstants;
import com.ly.common.core.domain.LoginUser;
import com.ly.common.core.domain.R;
import com.ly.common.core.domain.vo.LoginUserVO;
import com.ly.common.core.enums.ResultCode;
import com.ly.common.core.enums.UserIdentity;
import com.ly.common.core.enums.UserStatus;
import com.ly.common.core.utils.ThreadLocalUtil;
import com.ly.common.redis.service.RedisService;
import com.ly.common.security.exception.ServiceException;
import com.ly.common.security.service.TokenService;
import com.ly.friend.domain.user.User;
import com.ly.friend.domain.user.VO.UserVO;
import com.ly.friend.domain.user.dto.UserDTO;
import com.ly.friend.domain.user.dto.UserUpdateDTO;
import com.ly.friend.manager.user.UserCacheManager;
import com.ly.friend.mapper.user.UserMapper;
import com.ly.friend.service.user.IUserService;
import lombok.extern.slf4j.Slf4j;

import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;

@Service
@Slf4j
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UserCacheManager userCacheManager;

    @Value("${jwt.secret}")
    private String secret;

    @Value("${mail.code-expiration:10}")
    private Long emailCodeExpiration; // 验证码有效期（默认5分钟）

    @Value("${sms.send-limit:10}")
    private Integer sendLimit; // 每日发送上限

    @Value("${sms.is-send:false}")
    private boolean isSend; //验证码开关-> 打开:true 关闭:false

    @Value("${file.oss.downloadUrl}")
    private String download;

    private static final Long CODE_EXPIRE_MINUTES = 5L;

    @Override
    public boolean sendCode(UserDTO userDTO) {
        // 1. 参数校验
        String phone = userDTO.getPhone();
        validatePhone(phone);

        // 2. 频率控制检查
        checkRequestFrequency(phone);

        // 3. 每日限额检查
        checkDailyRequestLimit(phone);

        // 4. 生成并存储验证码
        String code = generateVerificationCode(phone);

        // 5. 发送验证码
        sendVerificationCode(phone, code);

        System.out.println("code: "+code);
        return true;

    }

    @Override
    public String codeLogin(String phone, String code) {
        //判断验证码是否正确
        CheckCode(phone,code);
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        if(user==null){   //新用户
            //注册逻辑 1.先完成验证码的比对->成功,往系统中增加用户
            user=new User();
            user.setPhone(phone);
            user.setStatus(UserStatus.Normal.getValue());
            userMapper.insert(user);
        }
        return tokenService.createToken(user.getUserId(),secret, UserIdentity.ORDINARY.getValue(),user.getNickName(),user.getHeadImage());
//        if(user!=null){   //老用户
//            String phoneCodeKey = CheckCode(phone, code);
//            //验证码已经比对成功,下一次会生成新的验证码,所以成功之后需要删除验证码
//            String phoneCodeKey = getPhoneCodeKey(phone);
//            String cacheCode = redisService.getCacheObject(phoneCodeKey,String.class);
//
//            if(StrUtil.isEmpty(cacheCode)){
//              throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
//            }
//            if(!cacheCode.equals(code)){
//              throw new ServiceException(ResultCode.FAILED_CODE_MISMATCH);
//            }
//            redisService.deleteObject(phoneCodeKey);
//            redisService.deleteObject(phoneCodeKey);
//            return tokenService.createToken(user.getUserId(),sercet, UserIdentity.ORDINARY.getValue(),user.getNickName());
//        }


    }

    private void CheckCode(String phone, String code) {
        String phoneCodeKey = getPhoneCodeKey(phone);
        String cacheCode = redisService.getCacheObject(phoneCodeKey,String.class);

        if(StrUtil.isEmpty(cacheCode)){
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        if(!cacheCode.equals(code)){
            throw new ServiceException(ResultCode.FAILED_CODE_MISMATCH);
        }
        redisService.deleteObject(phoneCodeKey);
    }

    @Override
    public boolean logout(String token) {
        //检查token是否为空
        if (StrUtil.isEmpty(token)) {
            throw new ServiceException(ResultCode.TOKEN_IS_DEFAULT);
        }
        //再处理前缀
        if (token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX,StrUtil.EMPTY);
        }

        return tokenService.deleteLoginUser(token,secret);

    }

    @Override
    public R<LoginUserVO> info(String token) {

//        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
//            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
//        }

        // 先检查token是否为空
        if (StrUtil.isEmpty(token)) {
            throw new ServiceException(ResultCode.TOKEN_IS_DEFAULT);
        }
//      再处理前缀
        if (token.startsWith(HttpConstants.PREFIX)) {
            token = token.substring(HttpConstants.PREFIX.length());
        }

        LoginUser loginUser = tokenService.getLoginUser(token, secret);

        if(loginUser==null){
            return R.fail();
        }
        LoginUserVO loginUserVO=new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if(StrUtil.isNotEmpty(loginUser.getHeadImage())){
            loginUserVO.setHeadImage(download+loginUser.getHeadImage());
        }
        return R.ok(loginUserVO);
    }

    @Override
    public UserVO detail() {
        Long userId= ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if(userId==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        UserVO userVO;
        userVO=userCacheManager.getUserById(userId);
        if(userVO==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if(StrUtil.isNotEmpty(userVO.getHeadImage())){
            userVO.setHeadImage(download+userVO.getHeadImage());
        }
        return userVO;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        User user = getUser();
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        //更新用户缓存
        extracted(user);
        return userMapper.updateById(user);
    }


    @Override
    public int updateHeadImage(String headImage) {
        User user = getUser();
        user.setHeadImage(headImage);
        userCacheManager.refreshUser(user);
        extracted(user);
        return userMapper.updateById(user);
    }

    private void extracted(User user) {
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(), user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
    }

    @NotNull
    private User getUser() {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        return user;
    }
    private void validatePhone(String phone) {
        if (StrUtil.isBlank(phone)) {
            throw new ServiceException(ResultCode.FAILED_USER_PHONE_EMPTY);
        }

        // 中国手机号正则（11位，1开头）
        String regex = "^1[3-9]\\d{9}$";
        if (!Pattern.matches(regex, phone)) {
            throw new ServiceException(ResultCode.FAILED_USER_PHONE_INVALID);
        }
    }

    private String getPhoneCodeKey(String phone) {
        return CacheConstants.PHONE_CODE_KEY + phone;
    }

    // 计数器 Key：实现业务级的每日限额控制，与验证码生命周期解耦。
    private String getCodeTimeKey(String phone) {
        return CacheConstants.CODE_TIME_KEY + phone;
    }


    private void checkRequestFrequency(String phone) {
        // getExpire()获取当前 Key 的剩余生存时间
        String phoneCodeKey = getPhoneCodeKey(phone);
        Long expire = redisService.getExpire(phoneCodeKey, TimeUnit.SECONDS);
        // 如果上一次验证码的发送时间距离现在 不足1分钟（即 总有效期 - 剩余时间 < 60秒），则拒绝新请求。
        if (expire != null && (emailCodeExpiration * 60 - expire) < 60) {
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }
    }

    private void checkDailyRequestLimit(String phone) {
        // 每天的验证码获取次数有一个限制  50 次 , 第二天  计数清0 重新开始计数
        // 计数  怎么存,存在哪里 ?
        // 操作次数数据频繁,不需要存储,  记录的次数  有效时间(当天有效) redis String  key: c:t:
        // 1.获取已经请求的次数 和 50 进行比较
        String codeTimeKey = getCodeTimeKey(phone);
        String sendTimesStr = redisService.getCacheObject(codeTimeKey, String.class);
        Long sendTimes = sendTimesStr != null ? Long.parseLong(sendTimesStr) : 0L;
        // 如果大于限制,抛出异常
        // 如果不大于限制,正常执行后续逻辑,并且将获取计数+1
        if (sendTimes >= sendLimit) {
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }
    }
    private String generateVerificationCode(String phone) {
        String code = isSend?RandomUtil.randomNumbers(6) : Constants.DEFAULT_CODE;
        String phoneCodeKey = getPhoneCodeKey(phone);
        String counterKey = getCodeTimeKey(phone);
        log.info("生成的验证码为: {}", code);

        // 存储验证码，设置过期时间
        redisService.setCacheObject(phoneCodeKey, code, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

        //判断发不发送验证码
        //由于这里阿里云申请失败就算了~

        // 先检查 Key 是否存在，不存在则初始化
        if (!redisService.hasKey(counterKey)) {
            redisService.setCacheObject(counterKey, "0");
        }
        // 更新计数器
        Long newCount = redisService.increment(counterKey);

        // 如果是当天第一次请求，设置计数器过期时间为当日剩余时间
        if (newCount == 1) {
            setDailyCounterExpiration(counterKey);
        }
        return code;
    }

    private void setDailyCounterExpiration(String counterKey) {
        // 处理当天首次短信验证码请求时的 Redis Key 过期时间设置
        LocalDateTime midnight = LocalDateTime.now()
                .plusDays(1)
                .withHour(0)
                .withMinute(0)
                .withSecond(0);
        // 计算过期时间  当前时间到 次日零点 的剩余秒数。
        long secondsUntilMidnight = ChronoUnit.SECONDS.between(
                LocalDateTime.now(), midnight);
        // 设置 Redis 过期时间
        // 将codeTimeKey 的过期时间设为 当天剩余秒数，确保 Key 在 当日 23:59:59 自动失效
        redisService.expire(counterKey, secondsUntilMidnight, TimeUnit.SECONDS);
    }

    private void sendVerificationCode(String phone, String code) {
        // 这里可以实现具体的发送验证码逻辑，比如调用短信服务或邮件服务
        log.info("向手机号 {} 发送验证码: {}", phone, code);
    }


}